package dsx.dsxFrame;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;

import dsx.biz._Biz;
import dsx.inf.IBizServer;
import dsx.inf.IConverter;
import dsx.inf.IDataTransfer;
import dsx.inf.INetServer;
import dsx.inf.ISlicer;
import dsx.macro.StringTool;
import dsx.system.ThreadEx;

public class RPCServer implements IDataTransfer {
    IBizServer bizServer = null;
    IConverter converter = null;
    INetServer netServer = null;
    ISlicer slicer = null;
    _RPCServer config = null;

    public int getStatus() {
        synchronized (this) {
            if (this.bizServer != null)
                return this.bizServer.getStatus();
            else
                return 0;
        }
    }

    public _RPCServer getConfig() {
        synchronized (this) {
            if (config == null)
                return null;
            if (this.netServer != null)
                this.config.netServerConfig = this.netServer.getConfig();
            if (this.bizServer != null)
                this.config.status = bizServer.getStatus();
            return this.config;
        }
    }

    public ArrayList<_Biz> getBizList() {
        synchronized (this) {
            if (bizServer != null)
                return bizServer.getBizList();
            else
                return null;
        }
    }

    public void init(_RPCServer config) {
        synchronized (this) {
            try {
                if (this.config != null && this.config.equals(config))
                    return;
                boolean started = false;
                if (bizServer != null) {
                    if (bizServer.getStatus() == 1) {
                        stopNoSync();
                        started = true;
                    }
                }
                this.config = config;
                createSlicer();
                createConverter();
                createNetServer();
                createBizServer();
                if (netServer != null)
                    this.netServer.init(config.netServerName, this, this.slicer);
                this.bizServer.init(config.bizServerName, config.path, this,
                        this.converter);
                if (config.autoStart == 1 || started)
                    startNoSync();
            } catch (Exception ex) {

            }
        }
    }

    public boolean start() {
        synchronized (this) {
            return startNoSync();
        }
    }

    public void stop() {
        synchronized (this) {
            stopNoSync();
        }
    }

    public boolean restart() {
        synchronized (this) {
            stopNoSync();
            return startNoSync();
        }
    }

    private boolean startNoSync() {
        if (bizServer == null)
            return false;
        if (netServer!= null) {
            if (!netServer.start()) {
                return false;
            }
        }
        if (!bizServer.start()) {
            if (netServer!= null) {
                netServer.stop();
            }
            return false;
        } else {
            return true;
        }
    }

    private void stopNoSync() {
        if (bizServer != null) {
            bizServer.stop();
            while (bizServer.getStatus() != 0) {
                ThreadEx.sleepEx(20);
            }
        }
        if (netServer != null)
            netServer.stop();
        synchronized (workers) {
            workers.notifyAll();
        }
        for (WorkerThread worker : workers)
            worker.stopEx();
        workers.clear();
        dataQueue.clear();
    }

    private void createSlicer() throws Exception {
        if (StringTool.isTrimEmpty(config.slicerClass)) {
            slicer = null;
        } else {
            slicer = (ISlicer) Class.forName(config.slicerClass)
                    .newInstance();
        }
    }

    private void createNetServer() throws Exception {
        if (StringTool.isEmpty(config.netServerClass)) {
            netServer = null;
        } else {
            netServer = (INetServer) Class.forName(config.netServerClass)
                    .newInstance();
        }
    }

    private void createConverter() throws Exception {
        if (StringTool.isEmpty(config.converterClass)) {
            converter = null;
        } else {
            converter = (IConverter) Class.forName(config.converterClass)
                    .newInstance();
        }
    }

    private void createBizServer() throws Exception {
        bizServer = (IBizServer) Class.forName(config.bizServerClass)
                .newInstance();
    }

    @Override
    public boolean recv(Object pipe, byte[] data) {
        try {
            synchronized (dataQueue) {
                DsxData dsxData = new DsxData();
                dsxData.pipe = pipe;
                dsxData.data = data;
                dataQueue.add(dsxData);
            }
            awakeWorker();
            return true;
        } catch (Exception ex) {
            return false;
        }
    }

    @Override
    public boolean send(Object pipe, byte[] data) {
        try {
            if (netServer != null) {
                netServer.send(pipe, data);
                return true;
            }
        } catch (Exception ex) {
        }
        return false;
    }

    Queue<DsxData> dataQueue = new LinkedList<DsxData>();

    List<WorkerThread> workers = new ArrayList<WorkerThread>();

    private void awakeWorker() {
        synchronized (workers) {
            if (workers.size() == 0
                    || (workers.size() < config.maxWorkerNum && dataQueue
                    .size() > 3)) {
                WorkerThread worker = new WorkerThread();
                worker.rpcServer = this;
                workers.add(worker);
                worker.start();
            } else {
                workers.notify();
            }
        }
    }

    private class DsxData {
        Object pipe;
        byte[] data;
    }

    private class WorkerThread extends ThreadEx {

        RPCServer rpcServer = null;

        @Override
        protected void doInLoop() {
            try {
                if (rpcServer != null && rpcServer.bizServer != null
                        && rpcServer.bizServer.getStatus() == 1) {
                    while (true) {
                        DsxData dsxData = null;
                        synchronized (rpcServer.dataQueue) {
                            dsxData = rpcServer.dataQueue.poll();
                        }
                        if (dsxData == null)
                            break;
                        rpcServer.bizServer.handle(dsxData.pipe, dsxData.data);
                    }
                }
                synchronized (rpcServer.workers) {
                    rpcServer.workers.wait();
                }
            } catch (Exception ex) {

            }
        }
    }

}
